﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Runtime.CompilerServices;
using Microsoft.Kinect;
using Microsoft.Kinect.Toolkit.Interaction;
using System.IO;
using jKinect.Connector;

namespace jKinect
{
    public class KinectManager
    {
        public KinectManagerConfig Config;

        KinectSensor _runtime;

        public KinectSensor Runtime
        {
            get { return _runtime; }
            set { _runtime = value; }
        }

        private bool _kinectIsRequested = false;

        public event EventHandler KinectReady;

        public InteractionStream InteractionStream;

        public event EventHandler<AllFramesReallyReadyEventArgs> SkeletonFrameReady
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            add
            {
                if (_runtime != null)
                    _runtime.AllFramesReady += _runtime_AllFramesReady;
            }
            [MethodImpl(MethodImplOptions.Synchronized)]
            remove
            {
                if (_runtime != null)
                    _runtime.AllFramesReady -= _runtime_AllFramesReady;
            }
        }

        void _runtime_AllFramesReady(object sender, AllFramesReadyEventArgs e)
        {
            using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
            {
                using (DepthImageFrame depthFrame = e.OpenDepthImageFrame())
                {
                    Config.SkeletonFrameReady(this, new AllFramesReallyReadyEventArgs(skeletonFrame, depthFrame));
                }
            }
        }

        public event EventHandler<InteractionFrameReadyEventArgs> InteractionFrameReady
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            add
            {
                if (_runtime != null)
                    InteractionStream.InteractionFrameReady += value;
            }
            [MethodImpl(MethodImplOptions.Synchronized)]
            remove
            {
                if (_runtime != null)
                    InteractionStream.InteractionFrameReady -= value;
            }
        }
        public KinectManager()
        {
            if (!IsKinectRuntimeInstalled) return;
            KinectSensor.KinectSensors.StatusChanged += new EventHandler<StatusChangedEventArgs>(Kinects_StatusChanged);
            if (KinectSensor.KinectSensors.Count > 0 && KinectSensor.KinectSensors[0].Status == KinectStatus.Connected)
                KinectSensor.KinectSensors[0].Stop();
        }

        public bool IsKinectRuntimeInstalled
        {
            get
            {
                bool isInstalled;
                try
                {
                    TestForKinectTypeLoadException();
                    isInstalled = true;
                }
                catch (FileNotFoundException)
                {
                    isInstalled = false;
                }
                return isInstalled;
            }
        }

        // This Microsoft.Kinect.dll based type, must be isolated in its own method
        // as the CLR will attempt to load the Microsoft.Kinect.dll assembly it when this method is executed.
        private void TestForKinectTypeLoadException()
        {
#pragma warning disable 219 //ignore the fact that status is unused code after this set.
            var status = KinectStatus.Disconnected;
#pragma warning restore 219
        }

        void Kinects_StatusChanged(object sender, StatusChangedEventArgs e)
        {
            switch (e.Status)
            {
                case KinectStatus.Connected:
                    if (_kinectIsRequested)
                        StartKinect(e.Sensor);
                    break;
                case KinectStatus.Disconnected:
                    break;
                case KinectStatus.Error:
                    break;
                case KinectStatus.NotPowered:
                    break;
                case KinectStatus.NotReady:
                    break;
                default:
                    break;
            }
        }

        public void InitKinect(KinectManagerConfig config)
        {
            _kinectIsRequested = true;
            this.Config = config;
            KinectSensor kinect = KinectSensor.KinectSensors.FirstOrDefault(k => k.Status == KinectStatus.Connected);
            if (kinect != null)
            {
                StartKinect(kinect);
            }
        }

        private void StartKinect(KinectSensor kinect)
        {
            try
            {
                _runtime = kinect;
                _runtime.Start();
                if (Config.UseSkeletonStream && !kinect.SkeletonStream.IsEnabled)
                {
                    _runtime.SkeletonStream.Enable(new TransformSmoothParameters()
                    {
                        Smoothing = 0.25f,
                        Correction = 0.0f,
                        Prediction = 0.0f,
                        JitterRadius = 0.05f,
                        MaxDeviationRadius = 0.04f
                    });
                    _runtime.DepthStream.Enable();
                    if (Config.UseInteractionStream)
                    {
                        InteractionStream = new InteractionStream(_runtime, new MyInteractionClient());
                        InteractionStream.InteractionFrameReady += Config.InteractionFrameReady;
                    }
                }
                if (Config.EventModel)
                    SkeletonFrameReady += Config.SkeletonFrameReady;
                else
                    SkeletonFrameReady -= Config.SkeletonFrameReady;
                if (KinectReady != null)
                    KinectReady(kinect, new EventArgs());
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "InitKinect Error");
            }
        }

        public void Uninitialize()
        {
            if (_runtime != null && _runtime.Status == KinectStatus.Connected)
                _runtime.Stop();
        }

    }

}
